﻿package com.zouloux.touch 
{
	import com.zouloux.core.base.DocumentBase;
	import com.zouloux.core.debug.Debug;
	import com.zouloux.utils.MathUtils;
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.events.Event;
	import flash.events.GestureEvent;
	import flash.events.MouseEvent;
	import flash.events.TouchEvent;
	import flash.events.TransformGestureEvent;
	import flash.geom.Point;
	import flash.ui.Multitouch;
	import flash.ui.MultitouchInputMode;
	/**
	 * ...
	 * @author ZoulouX
	 */
	public class GestureEmulator
	{
		/**
		 * Emuluer les GestureEvent avec le multitouch
		 * @param	pTarget : la cible
		 */
		public static function emulate (pTarget:DisplayObject):void
		{
			// Vérifier qu'on soit bien en mode multiTouch
			// Vérifier aussi si ce clip n'est pas déjà émulé (pour éviter les doublons et ouai)
			if (Multitouch.inputMode != MultitouchInputMode.GESTURE && !pTarget.hasEventListener(TouchEvent.TOUCH_BEGIN))
			{
				Debug.core(TouchEmulator, "GESTURE EMULATOR ADDED", arguments);
				
				// Créer un nouvel émulateur
				new GestureEmulator(pTarget);
			}
		}
		
		/**
		 * La cible à émuler
		 */
		protected var _target					:DisplayObject;
		
		/**
		 * Si on est en train de déplacer
		 */
		protected var _dragging					:Boolean				= false;
		
		/**
		 * Si on est en train de pincer
		 */
		protected var _pinch					:Boolean				= false;
		
		/**
		 * La dernière taille en pour 1
		 */
		protected var _lastScale				:Number;
		
		/**
		 * La dernière rotation
		 */
		protected var _lastRotation				:Number;
		
		/**
		 * L'identifiant du point principal
		 */
		protected var _firstPointID				:uint;
		
		/**
		 * L'identifiant du point de pincement
		 */
		protected var _secondPointID			:uint;
		
		/**
		 * Le position du point principal
		 */
		protected var _firstPointPosition		:Point;
		
		/**
		 * La position du point de pincement
		 */
		protected var _secondPointPosition		:Point;
		
		/**
		 * Le dernier centre du pincement
		 */
		protected var _lastCenter				:Point;
		
		/**
		 * La dernière position de déplacement
		 */
		protected var _lastPointPosition		:Point;
		
		
		/**
		 * Créer un nouvel emulateur
		 * @param	pTarget : la cible
		 */
		public function GestureEmulator (pTarget:DisplayObject)
		{
			// Mémoriser le clip
			_target = pTarget;
			
			// Ecouter les events de la souris
			pTarget.addEventListener(TouchEvent.TOUCH_BEGIN, touchStartHandler);
			DocumentBase.stage.addEventListener(TouchEvent.TOUCH_END, touchEndHandler);
			DocumentBase.stage.addEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler);
			
			// Ecouter lorsque le clip est supprimé pour virer les event
			pTarget.addEventListener(Event.REMOVED_FROM_STAGE, dispose);
		}
		
		/**
		 * Le touché à commencé
		 * @param	event
		 */
		protected function touchStartHandler (event:TouchEvent):void
		{
			// Si on est en pas en train de déplacer
			if (!_dragging)
			{
				// On déplace
				_dragging = true;
				
				// On enregistre l'id du point
				_firstPointID = event.touchPointID;
				
				// On déplace au moins une fois
				touchMoveHandler(event, true);
				
				// Le dernier déplacement c'est la position actuelle (pour calculer le delta)
				_lastPointPosition = _firstPointPosition;
			}
			else if (!_pinch)
			{
				// Si on pince pas, bah on pince, et ouai
				_pinch = true;
				
				// On enregistre l'id du second point
				_secondPointID = event.touchPointID;
				
				// On déplace au moins une fois
				touchMoveHandler(event, true);
				
				// On récupère la rotation de base
				_lastRotation = MathUtils.calculateAngle(_firstPointPosition, _secondPointPosition);
				
				// On récupère l'espacement du base
				_lastScale = MathUtils.calculateDistance(_firstPointPosition, _secondPointPosition);
				
				// On récupère le centre de base
				_lastCenter = new Point(
					(_firstPointPosition.x + _secondPointPosition.x) / 2,
					(_firstPointPosition.y + _secondPointPosition.y) / 2
				);
			}
		}
		
		/**
		 * Le touché à arrêté
		 * @param	event
		 */
		protected function touchEndHandler (event:TouchEvent):void
		{
			// Si c'est le premier point
			if (event.touchPointID == _firstPointID)
			{
				// Si on pinçait
				if (_pinch)
				{
					// On pince plus
					_pinch = false;
					
					// On inverse les points
					_firstPointID = _secondPointID;
					_lastPointPosition = _secondPointPosition;
					_firstPointPosition = _lastPointPosition;
				}
				else
					_dragging = false;
			}
			else if (event.touchPointID == _secondPointID)
			{
				// Si c'est le second point et qu'on pinçait, on pince plus tout simplement
				if (_pinch)
					_pinch = false;
			}
		}
		
		/**
		 * Le touché à bougé
		 * @param	event
		 */
		protected function touchMoveHandler (event:TouchEvent, pForce:Boolean = false):void
		{
			// Si c'est le premier point
			if (event.touchPointID == _firstPointID)
				_firstPointPosition = new Point(event.stageX, event.stageY);
			else if (event.touchPointID == _secondPointID)
				_secondPointPosition = new Point(event.stageX, event.stageY);
			
			// Actualiser la position
			if (!pForce && (event.touchPointID == _firstPointID || event.touchPointID == _secondPointID))
				updatePosition(event);
		}
		
		/**
		 * Actualiser la position
		 * @param	event
		 */
		protected function updatePosition (event:TouchEvent):void
		{
			// Pincer
			if (_pinch)
			{
				// Récupérer la rotation
				var currentRotation:Number = MathUtils.calculateAngle(_firstPointPosition, _secondPointPosition);
				
				// Récupérer la taille
				var currentScale:Number = MathUtils.calculateDistance(_firstPointPosition, _secondPointPosition);
				
				// Récupérer le centre
				var currentCenter:Point = new Point(
					(_firstPointPosition.x + _secondPointPosition.x) / 2,
					(_firstPointPosition.y + _secondPointPosition.y) / 2
				);
				
				// Dispatcher l'event de Zoom
				_target.dispatchEvent(new TransformGestureEvent(
					TransformGestureEvent.GESTURE_ZOOM,
					true,
					true,
					null,
					0, // X
					0, // Y
					currentScale / _lastScale, // scaleX
					currentScale / _lastScale, // scaleY
					currentRotation - _lastRotation, // rotation
					currentCenter.x - _lastCenter.x, // offsetX
					currentCenter.y - _lastCenter.y, // offsetY
					event.ctrlKey,
					event.altKey,
					event.shiftKey,
					event.commandKey,
					event.controlKey
				));
				
				// Dispatcher l'event de rotation
				_target.dispatchEvent(new TransformGestureEvent(
					TransformGestureEvent.GESTURE_ROTATE,
					true,
					true,
					null,
					0, // X
					0, // Y
					currentScale / _lastScale, // scaleX
					currentScale / _lastScale, // scaleY
					currentRotation - _lastRotation, // rotation
					currentCenter.x - _lastCenter.x, // offsetX
					currentCenter.y - _lastCenter.y, // offsetY
					event.ctrlKey,
					event.altKey,
					event.shiftKey,
					event.commandKey,
					event.controlKey
				));
				
				// Enregistrer les valeurs actuelles comme étant anciennes (pour les deltas)
				_lastRotation = currentRotation;
				_lastScale = currentScale;
				_lastCenter = currentCenter;
			}
			else if (_dragging)
			{
				// Si on déplace on dispatche l'event PAN
				_target.dispatchEvent(new TransformGestureEvent(
					TransformGestureEvent.GESTURE_PAN,
					true,
					true,
					null,
					0, // X
					0, // Y
					1, // scaleX
					1, // scaleY
					0, // rotation
					_firstPointPosition.x - _lastPointPosition.x, // offsetX
					_firstPointPosition.y - _lastPointPosition.y, // offsetY
					event.ctrlKey,
					event.altKey,
					event.shiftKey,
					event.commandKey,
					event.controlKey
				));
				
				// Enregistrer la position actuelle pour le delta
				_lastPointPosition = _firstPointPosition;
			}
		}
		
		/**
		 * Tuer cet émulateur
		 */
		protected function dispose (event:Event):void 
		{
			// Tuer tous les events
			_target.removeEventListener(Event.REMOVED_FROM_STAGE, dispose);
			_target.removeEventListener(TouchEvent.TOUCH_BEGIN, touchStartHandler);
			DocumentBase.stage.removeEventListener(TouchEvent.TOUCH_END, touchEndHandler);
			DocumentBase.stage.removeEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler);
		}
	}
}